import datetime
import os
import time
import traceback
import random
import base64
import json
import numpy
import requests
from loguru import logger
import zipfile
import hashlib
import re


class AKC:
    def __init__(self, playwright, host, app_id):
        self.app_id = app_id
        self.secret = "P4I8HCOP1OP7432XZVKR2MPJKKVQ7J"
        self.playwright = playwright
        self.host = host
        self.context = None
        self.browser = None
        self.page = None
        self.store_list = self.get_store_account('ikc', '')
        # self.store_list = [{'createTime': '2024-07-17 17:22:11', 'updateTime': '2024-07-17 17:22:11', 'id': 20, 'accountType': '结算账单和资金流水', 'storeKey': 'DY', 'storeId': 36, 'storeCode': '2137893979559548', 'platformName': None, 'platformStoreName': 'moodytiger儿童旗舰店', 'account': 'HOSTOYO儿童服装店', 'userName': 'hostoyo雁婷', 'voucher': 'DY04-01', 'pwd': 'Hwg888888', 'commercePlatformStoreCode': '2137893979559548', 'commercePlatformStoreName': None, 'mobile': '17399882029', 'messageKey': None}]
        self.index_url = "https://merc.aikucun.com/login.html"
        self.download_path = os.path.join(os.getcwd(), 'download')
        if not os.path.exists(self.download_path):
            os.makedirs(self.download_path)
        self.tj_account = self.get_tujian_account()

    def login(self, store):
        browser = self.playwright.chromium.launch(headless=False)
        self.context = browser.new_context(accept_downloads=True)
        self.page = self.context.new_page()
        self.page.goto(self.index_url, timeout=60000)
        self.wait(self.page)
        self.page.query_selector('input[placeholder="请输入用户名"]').fill(store['userName'])
        self.page.query_selector('input[type="password"]').fill(store['pwd'])
        time.sleep(1)
        self.page.query_selector('#login').click()
        # 检查是否有滑动验证码
        self.slide_code()
        # 再次检查滑动验证码
        self.check_slide_two()
        is_success = False
        for i in range(20):
            try:
                if self.page.query_selector('div.user-info:visible') is not None:
                    logger.info('登录成功...')
                    is_success = True
                    break
                time.sleep(1)
                logger.info(f'等待页面加载【{i+1}】秒...')
            except:
                pass
        if not is_success:
            raise Exception('登录失败')
        # self.close_dialog()

    def slide_code(self):
        has_code = False
        for k in range(10):
            try:
                if self.page.query_selector('#login-captcha:visible') is not None:
                    has_code = True
                break
            except:
                pass
        if not has_code:
            return
        logger.info("出现滑动验证码，开始滑动...")
        code_path = os.path.join(os.getcwd(), 'code.png')
        for k in range(20):
            try:
                if self.page.query_selector('#login-captcha:visible') is None:
                    break
                # 刷新验证码
                if self.page.query_selector('div.dx_captcha_basic_icon-btns:visible') is not None:
                    self.page.query_selector('div.dx_captcha_basic_icon-btns:visible').click()
                time.sleep(2)
                self.page.query_selector('#login-captcha:visible >> canvas').screenshot(path=code_path)
                time.sleep(0.5)
                dist = int(self.get_slider_xy(code_path))
                box = self.page.query_selector('div.dx_captcha_basic_slider-img-animated-wrap').bounding_box()
                track = self.get_tracks(dist + 5, 1, self.ease_out_quart)
                ix = int(box['x'] + box['width'] // 2)
                iy = int(box['y'] + box['height'] // 2)
                self.page.mouse.move(ix, iy)
                self.page.mouse.down(button='left')
                self.move_to_gap(self.page, ix, iy, track)
                time.sleep(1.5)
                logger.info("滑动完成...")
                time.sleep(5)
            except:
                traceback.print_exc()

    # 第二次滑动验证码检查
    def check_slide_two(self):
        has_code = False
        for k in range(10):
            try:
                if self.page.query_selector('div.dx_captcha_basic_bg:visible') is not None:
                    has_code = True
                break
            except:
                pass
        if not has_code:
            return
        logger.info("出现滑动验证码，开始滑动...")
        code_path = os.path.join(os.getcwd(), 'code.png')
        for k in range(20):
            try:
                if self.page.query_selector('div.dx_captcha_basic_bg:visible') is None:
                    break
                # 刷新验证码
                if self.page.query_selector('div.dx_captcha_basic_icon-btns:visible') is not None:
                    self.page.query_selector('div.dx_captcha_basic_icon-btns:visible').click()
                time.sleep(2)
                self.page.query_selector('div.dx_captcha_basic_bg:visible >> canvas').screenshot(path=code_path)
                time.sleep(0.5)
                dist = int(self.get_slider_xy(code_path))
                box = self.page.query_selector('div.dx_captcha_basic_slider-img-animated-wrap >> img:visible').bounding_box()
                track = self.get_tracks(dist + 10, 1, self.ease_out_quart)
                ix = int(box['x'] + box['width'] // 2)
                iy = int(box['y'] + box['height'] // 2)
                self.page.mouse.move(ix, iy)
                self.page.mouse.down(button='left')
                self.move_to_gap(self.page, ix, iy, track)
                time.sleep(1.5)
                logger.info("滑动完成...")
                time.sleep(5)
            except:
                traceback.print_exc()

    def ease_out_quart(self, x):
        return 1 - pow(1 - x, 4)

    def get_tracks(self, distance, seconds, ease_func):
        distance += 0
        tracks = [0]
        offsets = [0]
        for t in numpy.arange(0.0, seconds, 0.1):
            ease = ease_func
            offset = round(ease(t / seconds) * distance)
            tracks.append(offset - offsets[-1])
            offsets.append(offset)
        tracks.extend([-3, -2, -3, -2, -2, -2, -2, -1, -0, -1, -1, -1])
        return tracks

    def move_to_gap(self, page, ix, iy, track):
        """
        慢速移动鼠标
        :param ix:
        :param iy:
        :param track:
        :return:
        """
        f_y = [10, 12, 4, 6, 8, -4, -6, -8]
        mx = ix
        while track:
            x = track.pop(0)
            mx = mx + x
            page.mouse.move(mx, iy + random.choice(f_y))
            time.sleep(0.1)
        time.sleep(1)
        page.mouse.up()

    def close_dialog(self):
        for k in range(3):
            for c in range(3):
                try:
                    if self.page.query_selector('.topPopupClose:visible') is not None:
                        self.page.query_selector('.topPopupClose:visible').click(timeout=3000)
                        break
                    time.sleep(1)
                except:
                    pass
            for c in range(3):
                try:
                    if self.page.query_selector('button:has-text("知悉"):visible') is not None:
                        self.page.query_selector('button:has-text("知悉"):visible').click(timeout=3000)
                        break
                    time.sleep(1)
                except:
                    pass
            for c in range(3):
                try:
                    if self.page.query_selector('button:has-text("我已收到通知"):visible') is not None:
                        self.page.query_selector('button:has-text("我已收到通知"):visible').click(timeout=3000)
                        break
                    time.sleep(1)
                except:
                    pass
            for c in range(3):
                try:
                    if self.page.query_selector('button:has-text("关闭"):visible') is not None:
                        self.page.query_selector('button:has-text("关闭"):visible').click(timeout=3000)
                        break
                    time.sleep(1)
                except:
                    pass
            for c in range(3):
                try:
                    if self.page.query_selector('section.footer-dialog:visible >> i.el-icon-close:visible') is not None:
                        self.page.query_selector('section.footer-dialog:visible >> i.el-icon-close:visible').click(timeout=3000)
                        break
                    time.sleep(1)
                except:
                    pass
            for c in range(3):
                try:
                    if self.page.query_selector('div.guide_content >> div:has-text("我知道了"):visible') is not None:
                        self.page.query_selector('div.guide_content >> div:has-text("我知道了"):visible').click(timeout=3000)
                        break
                    time.sleep(1)
                except:
                    pass
            for c in range(3):
                try:
                    if self.page.query_selector('button:has-text("取消"):visible') is not None:
                        self.page.query_selector('button:has-text("取消"):visible').click(timeout=3000)
                        break
                    time.sleep(1)
                except:
                    pass


    def get_slider_xy(self, backImgPath):
        api = 'http://api.ttshitu.com/base64'
        with open(backImgPath, 'rb') as f:
            base64_data = base64.b64encode(f.read())
            b642 = base64_data.decode()
        params = {"typeid": "33", "image": b642, "username": self.tj_account['tjName'], "password": self.tj_account['tjPass']}
        headers = {'content-type': 'application/json;charset=UTF-8'}
        result = json.loads(requests.post(api, json=params, headers=headers).text)
        if result['success']:
            return result["data"]["result"]
        else:
            logger.info(result["message"])
            return result["data"]

    # 下载账单收款
    def download_receive_bill(self, yesterday):
        logger.info('点击到【账单收款查询】...')
        self.page.query_selector('div.menu-item:has-text("账单收款查询")').click()
        frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                frame = self.page.frame(url='https://merchant-cloud.aikucun.com/micro/fpsweb/10198/billNew/checkoutDetail')
                if frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if frame is None:
            raise Exception('页面加载失败')
        self.wait(frame)
        for k in range(10):
            try:
                if frame.query_selector('div.el-form-item:has(span:text-is("核销时间")) >> input[placeholder="开始时间"]') is not None:
                    break
                time.sleep(1)
            except:
                pass
        try:
            frame.query_selector('div.el-form-item:has(span:text-is("核销时间")) >> input[placeholder="开始时间"]').cick()
        except:
            frame.query_selector('div.el-form-item:has(span:text-is("核销时间")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始日期"]').fill(yesterday)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始时间"]').fill('00:00:00')
        time.sleep(1)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束日期"]').fill(yesterday)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束时间"]').fill('23:59:59')
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("确定")').click(timeout=3000)
        time.sleep(1)
        with self.page.expect_response('https://fpsmer.aikucun.com/bcs/clearing/report/queryBillDetail') as res:
            frame.query_selector('button:has-text("查询")').click()
        response = res.value
        if response.status == 200:
            if response.json()['data']['totalRecords'] == 0:
                logger.info('暂无数据')
                return None
        else:
            logger.info('查询失败')
            raise Exception('查询失败')
        logger.info(f"共【{response.json()['data']['totalRecords']}】条数据")
        time.sleep(1)
        logger.info('点击【导出明细】...')
        frame.query_selector('span:has-text("导出明细"):visible').click()
        time.sleep(2)
        frame.query_selector('div[aria-label="明细导出"] >> button:has-text("确 认")').click()
        time.sleep(2)
        logger.info('进入任务中心')
        task_frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                task_frame = self.page.frame(url='https://gray-merchant.aikucun.com/mx-merchant-web/task*')
                if task_frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if task_frame is None:
            raise Exception('任务中心页面加载失败')
        for k in range(10):
            try:
                if task_frame.query_selector('button[type="submit"]') is not None:
                    break
                time.sleep(1)
            except:
                pass
        tr_index = -1
        tr_list = task_frame.query_selector_all('table >> tbody.ant-table-tbody >> tr.ant-table-row')
        task_name = f'账单收付款明细导出-{datetime.datetime.now().strftime("%Y%m%d")}'
        for index, tr in enumerate(tr_list):
            task_name_text = tr.query_selector('td >> nth=0').text_content()
            if task_name in task_name_text:
                tr_index = index
                break
        # 判断有无下载按钮
        for t in range(180):
            with self.page.expect_response('https://merc.aikucun.com/web/v1/merchant/task/queryPageFileTask?*') as res:
                task_frame.query_selector('button[type="submit"]').click()
            response = res.value
            if tr_list[tr_index].query_selector('td >> button:has-text("下载"):visible') is not None:
                logger.info('任务完成，可进行下载')
                break
            time.sleep(10)
            logger.info('等待下载...')
        logger.info('点击下载导出')
        time.sleep(1)
        file_name = f'receive_bill_{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}'
        file_path = os.path.join(self.download_path, f'{file_name}.zip')
        with self.page.expect_download() as download_info:
            tr_list[tr_index].query_selector('td >> button:has-text("下载"):visible').click()
        download_obj = download_info.value
        # 删除旧文件
        if os.path.exists(file_path):
            os.remove(file_path)
        download_obj.save_as(file_path)
        # 等待文件下载完成
        for i in range(60):
            if os.path.exists(file_path):
                break
            time.sleep(1)
        if not os.path.exists(file_path):
            logger.info(traceback.format_exc())
            raise Exception(f"【{file_path}】下载失败")
        logger.info(f"【{file_path}】下载成功")
        unzip_path = os.path.join(self.download_path, file_name)
        bill_file = self.get_unzip_file(file_path, unzip_path)
        # 上传文件
        # uri = "api/v1/dyBill/dyBillRPAImport/{}".format(account_obj['storeCode'])
        # self.upload_file(uri, file_path, account_obj['accountType'], yesterday)
        # 删除压缩包
        # os.remove(file_path)
        return bill_file

    # 下载账单收款
    def get_receive_bill_count(self, month):
        logger.info('点击到【账单收款查询】...')
        self.page.query_selector('div.menu-item:has-text("账单收款查询")').click()
        frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                frame = self.page.frame(url='https://merchant-cloud.aikucun.com/micro/fpsweb/10198/billNew/checkoutDetail')
                if frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if frame is None:
            raise Exception('页面加载失败')
        self.wait(frame)
        for k in range(10):
            try:
                if frame.query_selector('div.el-form-item:has(span:text-is("核销时间")) >> input[placeholder="开始时间"]') is not None:
                    break
                time.sleep(1)
            except:
                pass
        try:
            frame.query_selector('div.el-form-item:has(span:text-is("核销时间")) >> input[placeholder="开始时间"]').cick()
        except:
            frame.query_selector('div.el-form-item:has(span:text-is("核销时间")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        start_date = month + '-01'
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始日期"]').fill(start_date)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始时间"]').fill('00:00:00')
        time.sleep(1)
        end_date = ((datetime.datetime.strptime(start_date, '%Y-%m-%d') + datetime.timedelta(days=31)).replace(day=1) - datetime.timedelta(days=1)).strftime('%Y-%m-%d')
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束日期"]').fill(end_date)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束时间"]').fill('23:59:59')
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("确定")').click(timeout=3000)
        time.sleep(1)
        with self.page.expect_response('https://fpsmer.aikucun.com/bcs/clearing/report/queryBillDetail') as res:
            frame.query_selector('button:has-text("查询")').click()
        response = res.value
        if response.status == 200:
            return response.json()['data']['totalRecords']
        else:
            logger.info('查询失败')
            raise Exception('查询失败')


    # 下载我的收款
    def download_owner_receive_bill(self, yesterday):
        logger.info('点击到【我的收款】...')
        self.page.query_selector('div.menu-item:has-text("我的收款")').click()
        frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                frame = self.page.frame(url='https://merchant-cloud.aikucun.com/micro/fpsweb/10100/settlement/wallet')
                if frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if frame is None:
            raise Exception('页面加载失败')
        self.wait(frame)
        # logger.info('选择【交易类型】为【余额提现】')
        # frame.query_selector('div.el-form-item:has(label[for="detailType"]) >> input').click()
        # time.sleep(1)
        # frame.query_selector('div.el-select-dropdown:visible >> li:has-text("余额提现")').click()
        # time.sleep(1)
        logger.info('选择【账户类型】为【平安账户】')
        frame.query_selector('div.el-form-item:has(label[for="accountType"]) >> input').click()
        time.sleep(1)
        frame.query_selector('div.el-select-dropdown:visible >> li:has-text("平安账户")').click()
        time.sleep(1)
        logger.info('选择交易时间')
        try:
            frame.query_selector('div.el-form-item:has(label:text-is("交易时间")) >> input[placeholder="开始时间"]').click()
        except:
            frame.query_selector('div.el-form-item:has(label:text-is("交易时间")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始日期"]').fill(yesterday)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始时间"]').fill('00:00:00')
        time.sleep(1)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束日期"]').fill(yesterday)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束时间"]').fill('23:59:59')
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("确定")').click(timeout=3000)
        time.sleep(1)
        with self.page.expect_response('https://fpsmer.aikucun.com/web/v1/pingan/account/withdraw/detail/query?*') as res:
            frame.query_selector('div.table >> button:has-text("查询")').click()
        response = res.value
        if response.status == 200:
            if response.json()['data'] is None:
                logger.info('暂无数据')
                return None
        else:
            logger.info('查询失败')
            raise Exception('查询失败')
        logger.info(f"共【{response.json()['data']['total']}】条数据")
        logger.info('点击【导出】...')
        file_path = os.path.join(self.download_path, f'owner_bill_{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}.xls')
        with self.page.expect_download() as download_info:
            frame.query_selector('span:has-text("导出"):visible').click()
        download_obj = download_info.value
        # 删除旧文件
        if os.path.exists(file_path):
            os.remove(file_path)
        download_obj.save_as(file_path)
        # 等待文件下载完成
        for i in range(60):
            if os.path.exists(file_path):
                break
            time.sleep(1)
        if not os.path.exists(file_path):
            logger.info(traceback.format_exc())
            raise Exception(f"【{file_path}】下载失败")
        logger.info(f"【{file_path}】下载成功")
        return file_path

    # 下载我的收款
    def get_owner_receive_bill_count(self, month):
        logger.info('点击到【我的收款】...')
        self.page.query_selector('div.menu-item:has-text("我的收款")').click()
        frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                frame = self.page.frame(url='https://merchant-cloud.aikucun.com/micro/fpsweb/10100/settlement/wallet')
                if frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if frame is None:
            raise Exception('页面加载失败')
        self.wait(frame)
        # logger.info('选择【交易类型】为【余额提现】')
        # frame.query_selector('div.el-form-item:has(label[for="detailType"]) >> input').click()
        # time.sleep(1)
        # frame.query_selector('div.el-select-dropdown:visible >> li:has-text("余额提现")').click()
        # time.sleep(1)
        logger.info('选择【账户类型】为【平安账户】')
        frame.query_selector('div.el-form-item:has(label[for="accountType"]) >> input').click()
        time.sleep(1)
        frame.query_selector('div.el-select-dropdown:visible >> li:has-text("平安账户")').click()
        time.sleep(1)
        logger.info('选择交易时间')
        try:
            frame.query_selector('div.el-form-item:has(label:text-is("交易时间")) >> input[placeholder="开始时间"]').click()
        except:
            frame.query_selector('div.el-form-item:has(label:text-is("交易时间")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        start_date = month + '-01'
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始日期"]').fill(start_date)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始时间"]').fill('00:00:00')
        time.sleep(1)
        end_date = ((datetime.datetime.strptime(start_date, '%Y-%m-%d') + datetime.timedelta(days=31)).replace(day=1) - datetime.timedelta(days=1)).strftime('%Y-%m-%d')
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束日期"]').fill(end_date)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束时间"]').fill('23:59:59')
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("确定")').click(timeout=3000)
        time.sleep(1)
        with self.page.expect_response('https://fpsmer.aikucun.com/web/v1/pingan/account/withdraw/detail/query?*') as res:
            frame.query_selector('div.table >> button:has-text("查询")').click()
        response = res.value
        if response.status == 200:
            if response.json()['data'] is None:
                return 0
            else:
                return response.json()['data']['total']
        else:
            logger.info('查询失败')
            raise Exception('查询失败')

    # 下载业务结算明细
    def download_business_detail(self, yesterday):
        logger.info('点击到【业务结算明细查询】...')
        self.page.goto('https://merchant-cloud.aikucun.com/micro/fpsweb/10196/billNew/businessDetail')
        # self.page.query_selector('div.menu-item:has-text("业务结算明细查询")').click()
        time.sleep(10)
        for c in range(3):
            try:
                if self.page.query_selector('button:has-text("关闭"):visible') is not None:
                    self.page.query_selector('button:has-text("关闭"):visible').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                pass
        frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                frame = self.page.frame(url='https://merchant-cloud.aikucun.com/micro/fpsweb/10196/billNew/businessDetail')
                if frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if frame is None:
            raise Exception('页面加载失败')
        self.wait(frame)
        for k in range(10):
            try:
                if frame.query_selector('div.el-form-item:has(span:text-is("业务发生时间")) >> input[placeholder="开始时间"]') is not None:
                    break
                time.sleep(1)
            except:
                pass
        self.page.query_selector('a.merchant_login_no_register').hover()
        time.sleep(2)
        logger.info('点击【结算明细数据】')
        with self.page.expect_response('https://fpsmer.aikucun.com/bcs/clearing/report/queryBillTypeDetail*') as res:
            frame.query_selector('#tab-billSettlement').click()
        response = res.value
        time.sleep(1)
        logger.info('清空【业务发生时间】')
        try:
            frame.query_selector('div.el-form-item:has(span:text-is("业务发生时间")) >> input[placeholder="开始时间"]').click()
        except:
            frame.query_selector('div.el-form-item:has(span:text-is("业务发生时间")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("清空")').click(timeout=3000)
        time.sleep(1)
        logger.info('选择【账单日】')
        frame.query_selector('div.el-form-item:has(span:text-is("账单日")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始日期"]').fill(yesterday)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始时间"]').fill('00:00:00')
        time.sleep(1)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束日期"]').fill(yesterday)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束时间"]').fill('23:59:59')
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("确定")').click(timeout=3000)
        time.sleep(1)
        with self.page.expect_response('https://fpsmer.aikucun.com/bcs/clearing/report/queryBillTypeDetail*') as res:
            frame.query_selector('div.business_detail_wrap >> button:has-text("查询")').click()
        response = res.value
        logger.info('点击【明细汇总导出】...')
        frame.query_selector('button:has-text("明细汇总导出")').click()
        has_record = True
        for k in range(5):
            try:
                if frame.query_selector('div.el-notification:has-text("导出记录数为0"):visible') is not None:
                    logger.info('暂无数据')
                    has_record = False
                    break
                time.sleep(1)
            except:
                pass
        if not has_record:
            return None
        frame.query_selector('div[aria-label="明细汇总导出"] >> button:has-text("确 认")').click()
        self.wait(self.page)
        logger.info('进入任务中心')
        task_frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                task_frame = self.page.frame(url='https://gray-merchant.aikucun.com/mx-merchant-web/task*')
                if task_frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if task_frame is None:
            raise Exception('任务中心页面加载失败')
        for k in range(10):
            try:
                if task_frame.query_selector('button[type="submit"]') is not None:
                    break
                time.sleep(1)
            except:
                pass
        time.sleep(3)
        tr_index = -1
        tr_list = task_frame.query_selector_all('table >> tbody.ant-table-tbody >> tr.ant-table-row')
        task_name = f'业务结算明细汇总导出-{datetime.datetime.now().strftime("%Y%m%d")}'
        for index, tr in enumerate(tr_list):
            task_name_text = tr.query_selector('td >> nth=0').text_content()
            if task_name in task_name_text:
                tr_index = index
                break
        # 判断有无下载按钮
        for t in range(180):
            with self.page.expect_response('https://merc.aikucun.com/web/v1/merchant/task/queryPageFileTask?*') as res:
                task_frame.query_selector('button[type="submit"]').click()
            response = res.value
            if tr_list[tr_index].query_selector('td >> button:has-text("下载"):visible') is not None:
                logger.info('任务完成，可进行下载')
                break
            time.sleep(10)
            logger.info('等待下载...')
        logger.info('点击下载')
        time.sleep(1)
        file_name = f'business_bill_{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}'
        file_path = os.path.join(self.download_path, f'{file_name}.zip')
        with self.page.expect_download() as download_info:
            tr_list[tr_index].query_selector('td >> button:has-text("下载"):visible').click()
        download_obj = download_info.value
        # 删除旧文件
        if os.path.exists(file_path):
            os.remove(file_path)
        download_obj.save_as(file_path)
        # 等待文件下载完成
        for i in range(60):
            if os.path.exists(file_path):
                break
            time.sleep(1)
        if not os.path.exists(file_path):
            logger.info(traceback.format_exc())
            raise Exception(f"【{file_path}】下载失败")
        logger.info(f"【{file_path}】下载成功")
        unzip_path = os.path.join(self.download_path, file_name)
        bill_file = self.get_unzip_file(file_path, unzip_path)
        return bill_file

    # 下载业务结算明细
    def get_business_detail_count(self, month):
        logger.info('点击到【业务结算明细查询】...')
        self.page.goto('https://merchant-cloud.aikucun.com/micro/fpsweb/10196/billNew/businessDetail')
        time.sleep(10)
        for c in range(3):
            try:
                if self.page.query_selector('button:has-text("关闭"):visible') is not None:
                    self.page.query_selector('button:has-text("关闭"):visible').click(timeout=3000)
                    break
                time.sleep(1)
            except:
                pass
        frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                frame = self.page.frame(url='https://merchant-cloud.aikucun.com/micro/fpsweb/10196/billNew/businessDetail')
                if frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if frame is None:
            raise Exception('页面加载失败')
        self.wait(frame)
        for k in range(10):
            try:
                if frame.query_selector('div.el-form-item:has(span:text-is("业务发生时间")) >> input[placeholder="开始时间"]') is not None:
                    break
                time.sleep(1)
            except:
                pass
        self.page.query_selector('a.merchant_login_no_register').hover()
        time.sleep(2)
        logger.info('点击【结算明细数据】')
        with self.page.expect_response('https://fpsmer.aikucun.com/bcs/clearing/report/queryBillTypeDetail*') as res:
            frame.query_selector('#tab-billSettlement').click()
        response = res.value
        time.sleep(1)
        logger.info('清空【业务发生时间】')
        try:
            frame.query_selector('div.el-form-item:has(span:text-is("业务发生时间")) >> input[placeholder="开始时间"]').click()
        except:
            frame.query_selector('div.el-form-item:has(span:text-is("业务发生时间")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("清空")').click(timeout=3000)
        time.sleep(1)
        logger.info('选择【账单日】')
        frame.query_selector('div.el-form-item:has(span:text-is("账单日")) >> input[placeholder="开始时间"]').click()
        time.sleep(1)
        start_date = month + '-01'
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始日期"]').fill(start_date)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="开始时间"]').fill('00:00:00')
        time.sleep(1)
        end_date = ((datetime.datetime.strptime(start_date, '%Y-%m-%d') + datetime.timedelta(days=31)).replace(day=1) - datetime.timedelta(days=1)).strftime('%Y-%m-%d')
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束日期"]').fill(end_date)
        frame.query_selector('div.el-date-range-picker:visible >> input[placeholder="结束时间"]').fill('23:59:59')
        time.sleep(1)
        frame.query_selector('div.el-picker-panel__footer:visible >> button:has-text("确定")').click(timeout=3000)
        time.sleep(1)
        with self.page.expect_response('https://fpsmer.aikucun.com/bcs/clearing/report/queryBillTypeDetail*') as res:
            frame.query_selector('div.business_detail_wrap >> button:has-text("查询")').click()
        response = res.value
        logger.info('点击【明细汇总导出】...')
        frame.query_selector('button:has-text("明细汇总导出")').click()
        has_record = True
        for k in range(5):
            try:
                if frame.query_selector('div.el-notification:has-text("导出记录数为0"):visible') is not None:
                    logger.info('暂无数据')
                    has_record = False
                    break
                time.sleep(1)
            except:
                pass
        if not has_record:
            return None
        frame.query_selector('div[aria-label="明细汇总导出"] >> button:has-text("确 认")').click()
        self.wait(self.page)
        logger.info('进入任务中心')
        task_frame = None
        for i in range(10):
            try:
                self.page.query_selector('div')
                task_frame = self.page.frame(url='https://gray-merchant.aikucun.com/mx-merchant-web/task*')
                if task_frame is not None:
                    break
                time.sleep(1)
            except:
                pass
        if task_frame is None:
            raise Exception('任务中心页面加载失败')
        for k in range(10):
            try:
                if task_frame.query_selector('button[type="submit"]') is not None:
                    break
                time.sleep(1)
            except:
                pass
        time.sleep(3)
        tr_index = -1
        tr_list = task_frame.query_selector_all('table >> tbody.ant-table-tbody >> tr.ant-table-row')
        task_name = f'业务结算明细汇总导出-{datetime.datetime.now().strftime("%Y%m%d")}'
        for index, tr in enumerate(tr_list):
            task_name_text = tr.query_selector('td >> nth=0').text_content()
            if task_name in task_name_text:
                tr_index = index
                break
        # 判断有无下载按钮
        remark = None
        for t in range(180):
            with self.page.expect_response('https://merc.aikucun.com/web/v1/merchant/task/queryPageFileTask?*') as res:
                task_frame.query_selector('button[type="submit"]').click()
            response = res.value
            if tr_list[tr_index].query_selector('td >> button:has-text("下载"):visible') is not None:
                logger.info('任务完成，可进行下载')
                remark = tr_list[tr_index].query_selector('td >> nth=6').text_content()
                break
            time.sleep(10)
            logger.info('等待下载...')
        pattern = re.compile(r'总记录数\n?([0-9]*)')
        res = pattern.search(remark)
        bill_count = res.group(1)
        return bill_count


    def get_all_files(self, path, file_list):
        if not os.path.exists(path):
            return
        for file in os.listdir(path):
            if os.path.isfile(os.path.join(path, file)):
                file_list.append(os.path.join(path, file))
            else:
                self.get_all_files(os.path.join(path, file), file_list)

    def get_unzip_file(self, file_path, path):
        # 删除旧文件
        file_list = []
        self.get_all_files(path, file_list)
        for file in file_list:
            os.remove(file)
        # 解压文件
        self.unzip(file_path, path)
        for file in os.listdir(path):
            return os.path.join(path, file)
        return None

    def unzip(self, zip_path, extract_path):
        with zipfile.ZipFile(zip_path, 'r') as zip_ref:
            zip_ref.extractall(extract_path)

    def get_store_account(self, platform, type):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/storeAccount/getAccountList/{platform}?appId={self.app_id}&type={type}&sign={sign.upper()}&timestamp={timestamp}"
        print(url)
        res = requests.get(url)
        logger.info(res.text)
        if res.status_code == 200:
            return res.json()['data']

    def upload_file(self, api_url, coding_param, file_list):
        try:
            timestamp = int(time.time() * 1000)
            text = "{}{}{}".format(self.app_id, timestamp, self.secret)
            sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
            logger.info("正在上传文件到服务器")
            url = self.host + api_url + f"?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
            files = []
            for file in file_list:
                files.append(('file', (open(file, 'rb'))))
            logger.info(f'上传文件到服务器url：{url}')
            response = requests.request("POST", url, files=files, headers={'codingParam': coding_param}, timeout=180)
            logger.info('上传文件到服务器结果：' + response.text)
        except:
            logger.info(traceback.format_exc())

    # 更新流程日志状态
    def save_log_record(self, data):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/logRecord/saveLog?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        logger.info(f'保存日志参数：{data}')
        res = requests.post(url, json=data)
        logger.info(f'保存日志结果：{res.text}')
        if res.status_code == 200:
            return res.json()['data']

    # 获取任务ID
    def get_job_id(self, key):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/storeAccount/getJobId/{key}?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        res = requests.get(url)
        logger.info(res.text)
        if res.status_code == 200:
            return res.json()['data']

    def wait(self, page, seconds=1):
        try:
            page.wait_for_load_state(state="domcontentloaded")
            time.sleep(seconds)
        except:
            pass

    def close(self):
        try:
            if self.page:
                self.page.close()
        except:
            pass
        try:
            if self.browser:
                self.browser.close()
        except:
            pass
        try:
            if self.context:
                self.context.close()
        except:
            pass

    def kill(self):
        os.system('taskkill /F /IM chrome.exe')
        time.sleep(3)

    # 店铺按照userName分组
    def group_store(self, store_list):
        group_store = {}
        for store in store_list:
            if store['mobile'] in group_store.keys():
                item = list(group_store[store['mobile']])
                item.append(store)
                group_store[store['mobile']] = item
            else:
                item = [store]
                group_store[store['mobile']] = item
        return group_store

    # 获取图鉴账号密码
    def get_tujian_account(self):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/tjAccount/findOne?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        res = requests.get(url)
        if res.status_code == 200:
            return res.json()['data']

    def encode_param(self, data_list):
        timestamp = int(time.time() * 1000)
        text = "{}{}{}".format(self.app_id, timestamp, self.secret)
        sign = hashlib.md5(text.encode(encoding='UTF-8')).hexdigest()
        url = self.host + f"api/v1/storeAccount/codingParams?appId={self.app_id}&sign={sign.upper()}&timestamp={timestamp}"
        res = requests.post(url, data=json.dumps(data_list), headers={'content-type': 'application/json'})
        if res.status_code == 200:
            return res.json()['data']